#!/usr/bin/env python

#  Copyright (c) 2010
#  The Regents of the University of Michigan
#  All Rights Reserved

#  Permission is granted to use, copy, create derivative works, and
#  redistribute this software and such derivative works for any purpose,
#  so long as the name of the University of Michigan is not used in
#  any advertising or publicity pertaining to the use or distribution
#  of this software without specific, written prior authorization. If
#  the above copyright notice or any other identification of the
#  University of Michigan is included in any copy of any portion of
#  this software, then the disclaimer below must also be included.

#  This software is provided as is, without representation or warranty
#  of any kind either express or implied, including without limitation
#  the implied warranties of merchantability, fitness for a particular
#  purpose, or noninfringement.  The Regents of the University of
#  Michigan shall not be liable for any damages, including special,
#  indirect, incidental, or consequential damages, with respect to any
#  claim arising out of or in connection with the use of the software,
#  even if it has been or is hereafter advised of the possibility of
#  such damages.

import os
import signal
import subprocess
import sys
import time

CLEARVIEW_PATH = os.environ.get('CLEARVIEW_DIR')
if CLEARVIEW_PATH is None:
  CLEARVIEW_PATH = os.environ.get('HOME') + '/clearview'
CLEARVIEW_PATH += '/clearview'

HOP_INTERVAL = 30 * 60 # Hop every thirty minutes.

class Hopper(object):
  '''Manages moving Clearview between port_ranges.'''
  def __init__(self, first_range_start, last_range_start, range_size):
    self.first_range_start = first_range_start
    self.last_range_start = last_range_start
    self.range_size = range_size

  def _take_down_clearview(self):
    if self.clearview.poll() is not None:
      print "ERROR: Clearview died early!"
      sys.exit(1)

    self.clearview.terminate()
    print 'Waiting for ClearView...',
    sys.stdout.flush()
    self.clearview.wait()
    print 'Done!'

  def get_current_hop_num(self):
    ret = (self.current_start_port - self.first_range_start) / self.range_size
    ret += 1  #1-based
    return ret

  def _start_clearview(self):
    dirname = 'hop%d' % self.get_current_hop_num()
    os.mkdir(dirname)

    ch_stdout = open('%s/stdout' % dirname, 'w')
    ch_stderr = open('%s/stderr' % dirname, 'w')
    start_port = self.current_start_port
    end_port = self.current_start_port + self.range_size - 1

    command = [CLEARVIEW_PATH, IP_STRING, str(start_port), str(end_port)]

    print 'Running command: %s' % ' '.join(str(x) for x in command)
    
    self.clearview = subprocess.Popen(command, stdout=ch_stdout,
                                      stderr=ch_stderr, cwd=dirname, shell=False)
    if self.clearview.poll() is not None:
      print 'ERROR: Clearview crashed on startup!'
      sys.exit(1)

  def start_hopping(self):
    for x in range(self.first_range_start, self.last_range_start + self.range_size,
                   self.range_size):
      self.current_start_port = x
      self._start_clearview()
      assert HOP_INTERVAL >= 60
      time.sleep(59)
      if self.clearview.poll() is not None:
        print 'ERROR: Clearview crashed!'
        sys.exit(1)
      time.sleep(HOP_INTERVAL-59)
      self._take_down_clearview()

if __name__ == '__main__':
  if len(sys.argv) != 5:
    print ('Usage: %s external_ip_address first_range_start ' +
           'last_range_start range_size') % sys.argv[0]
    sys.exit(1)
  global IP_STRING
  IP_STRING = sys.argv[1]
  hopper = Hopper(int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4]))
  hopper.start_hopping()
